using System;
using Gtk;
using ProtoBuf;
using JiveMessengerLinux;
using System.IO;
using NLog.Config;
using NLog.Targets;
using NetworkCommsDotNet;
using JmShared;
using System.Threading;
using System.Reflection;
using Gdk;
using System.Collections.Generic;
using System.Linq;
using MessageType = JmShared.MessageType;

namespace JiveMessengerLinux
{
    public partial class MainWindow : Gtk.Window
    {
        private ChatControl ChatControl = null;
        private bool _firstConnect = true;

        private Dictionary<string, PrivateMessage> OpenPmWindows { get; set; }

        public MainWindow()
            : base(Gtk.WindowType.Toplevel)
        {

            this.Shown += HandleShown;
            SetLogging();
            Build();
            SetStartup();
            this.LoginWidget.LoginEvent += HandleLoginEvent;
            this.bnConfig.Clicked += new EventHandler(bnConfig_Clicked);

            Client.ClientDisconnectedEvent += HandleClientDisconnectedEvent;
            Client.ProcessMessageEvent += HandleProcessMessageEvent;
            this.WindowStateEvent += stateChanged;
            GLib.ExceptionManager.UnhandledException += HandleUnhandledException;
            avatarBox.BorderWidth = 5;

            this.bnConfig.Sensitive = false;


            //CreateOptionsButton();
            ChatControl = new ChatControl();
            ChatControl.WriteMessageEvent += SendMessageEvent;
            ChatControl.NowWritingEvent += SendNowWritingUpdate;
            ChatControl.NudgeEvent += (s, e) =>
                {
                    Client.SendStatusMessage(MessageType.Nudge, "");
                    this.Nudge();
                    this.DisplayMessage("You have sent a nudge!", Client.CurrentUserInfo, MessageType.Nudge);
                };
            ChatControl.Visible = true;
            this.Show();
            if (Client.ConnectedClients == null)
                Client.ConnectedClients = new Dictionary<string, UserInfo>();
            if (Client.TextTags == null)
                Client.TextTags = new Dictionary<string, Gtk.TextTag>();
            if (Client.TextTagCount == null)
                Client.TextTagCount = new Dictionary<string, int>();


            this.ChatControl.AddTextTag(Client.GreenUser);
            this.ChatControl.AddTextTag(Client.GreyUser);
            this.ChatControl.AddTextTag(Client.RedUser);

            if (StaticSettings.AutoLogin)
            {
                Thread t = new Thread(() =>
                    {
                        Thread.Sleep(1500);
                        StartConnectThread();
                    });
                t.Start();
            }

        }



        private void bnConfig_Clicked(object sender, EventArgs e)
        {
            Menu menu = new Menu();
            //log in
            MenuItem item = new MenuItem("Log in");
            item.ButtonPressEvent += new ButtonPressEventHandler((s, args) =>
                {
                    StartConnectThread();
                    Login.IsUserConnecting = true;
                });
            item.Sensitive = !Client.IsLoggedIn;
            menu.Add(item);
            //log out
            item = new MenuItem("Log out");
            item.ButtonPressEvent += new ButtonPressEventHandler((s, args) =>
                {
                    Login.IsUserConnecting = true;
                    Disconnect();
                });
            item.Sensitive = Client.IsLoggedIn;
            menu.Add(item);
            //add user empoticons
            item = new MenuItem("Add user emoticons");
            item.ButtonPressEvent += new ButtonPressEventHandler((s, args) =>
                {
                    AddUserEmoticons aue = new AddUserEmoticons(StaticSettings.CustomContactEmoticons, StaticSettings.CustomEmoticons, StaticSettings.CustomEmoticonFolder);
                    aue.Modal = true;
                    aue.Show();
                });
            menu.Add(item);
            //options
            item = new MenuItem("Options");
            item.ButtonPressEvent += new ButtonPressEventHandler((s, args) =>
                {
                    Options opt = new Options();
                    opt.Show();
                });
            menu.Add(item);
            item = new MenuItem("Exit");
            item.ButtonPressEvent += new ButtonPressEventHandler((s, args) =>
                {
                    ProcessEvent(Gdk.EventHelper.New(Gdk.EventType.Delete));
                });
            menu.Add(item);

            menu.ShowAll();
            menu.Popup();
        }

        //private void MoveConfigMenu(Menu m)
        //{
        //    int windowX;
        //    int windowY;
        //    Button bn = this.bnConfig;
        //  //  Menu win = EmoticonWin;
        //    bn.GdkWindow.GetOrigin(out windowX, out windowY);



        //    windowX += bn.Allocation.X;
        //    windowY += bn.Allocation.Y;
        //    //int height;
        //    //int width;
        //    Gtk.Requisition r = m.SizeRequest();
        //    //win.GetSize(out width, out height);
        //    //this.EmoticonWin.Resize(r.Height, r.Width);
        //    windowY -= r.Height;

        //    m.Move(windowX, windowY);
        //}


        //private void CreateOptionsButton()
        //{


        //}
        /// <summary>
        /// Disconnects from the server
        /// </summary>
        /// <returns></returns>
        private void Disconnect()
        {
            if (Client.MasterConnectionInfo.ConnectionState != ConnectionState.Established) return;

            TCPConnection.GetConnection(Client.MasterConnectionInfo).CloseConnection(false);
            if (Client.MasterConnectionInfo.ConnectionState == ConnectionState.Shutdown)
            {
                // this.DisplayMessage(Client.CurrentUserInfo.PrettyName + " has left the conversation!", Client.CurrentUserInfo, MessageType.Logout);
            }
            else
            {
                this.DisplayMessage("Not Disconnected! ", Client.CurrentUserInfo, MessageType.Error);
            }

            //this.avatarBox = new VBox();
            foreach (var v in this.avatarBox.Children)
                this.avatarBox.Remove(v);

        }

        private void HandleShown(object sender, EventArgs e)
        {





        }

        private void stateChanged(object sender, WindowStateEventArgs a)
        {
            Gdk.EventWindowState ews = a.Event;
            WindowState newWs = ews.NewWindowState;
            if (newWs == WindowState.Iconified)
            {
                StaticSettings.IsMaximized = false;
            }
            else if (newWs == WindowState.Maximized)
            {
                StaticSettings.IsMaximized = true;
            }
            else
            {
                StaticSettings.IsMaximized = false;
            }
        }

        private void SetStartup()
        {

            OpenPmWindows = new Dictionary<string, PrivateMessage>();


            //            if (Properties.Settings.Default.Font == null)
            //                Properties.Settings.Default.Font = System.Drawing.SystemFonts.DefaultFont;
            //            if (Properties.Settings.Default.Color.IsEmpty)
            //                Properties.Settings.Default.Color = System.Drawing.Color.Black;

            //            this.Top = Properties.Settings.Default.Top;
            //            this.Left = Properties.Settings.Default.Left;
            //            this.Height = Properties.Settings.Default.Height;
            //            this.Width = Properties.Settings.Default.Width;
            if (StaticSettings.WindowX > -1 && StaticSettings.WindowY > -1)
                this.Move(StaticSettings.WindowX, StaticSettings.WindowY);
            if (StaticSettings.WindowSizeX > -1 && StaticSettings.WindowSizeY > -1 && (!StaticSettings.IsMaximized))
                this.Resize(StaticSettings.WindowSizeX, StaticSettings.WindowSizeY);
            // Very quick and dirty - but it does the job
            if (StaticSettings.SettingStartMinimized)
                this.Iconify();
            else if (StaticSettings.IsMaximized)
                this.Maximize();

            if (!Directory.Exists(StaticSettings.DataFolder))
                Directory.CreateDirectory(StaticSettings.DataFolder);

        }

        private void HandleUnhandledException(GLib.UnhandledExceptionArgs args)
        {
            Exception err = args.ExceptionObject as Exception;
            MessageBox.Show(err.ToString());

        }

        private void AddPMEventHandlers(PrivateMessage pm)
        {
            //        pm.AddHandler(PrivateMessage.SetOptionEvent, new SetOptionsEventHandler(SetOptionsEvent));
            pm.WriteMessageEvent += SendMessageEvent;
            pm.ClosePmEvent += ClosePrivateMessageEvent;
            pm.NowWritingEvent += SendNowWritingUpdate;
        }

        private void HandleLoginEvent(object sender, Login.LoginEventArgs e)
        {

            StartConnectThread();
        }

        /// <summary>
        /// Sends a status update when you are typing.
        /// </summary>
        private void SendNowWritingUpdate(object sender, EventArgs args)
        {
            Client.SendStatusMessage(JmShared.MessageType.IsWriting, sender.GetType() == typeof (PrivateMessage) ? "PM" : "");
        }

        /// <summary>
        /// Triggers when a PrivateMessage window is closed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ClosePrivateMessageEvent(object sender, ClosePMEventArgs e)
        {
            if (this.OpenPmWindows.ContainsKey(e.User.UserName))
            {
                this.OpenPmWindows.Remove(e.User.UserName);
            }
        }

        /// <summary>
        /// Sends the ChatMessage to the server
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SendMessageEvent(object sender, WriteMessageEventArgs e)
        {
            if (!Client.SendMessage(e.Message))
                this.DisplayMessage(String.Format("Could not deliver message: \r\n{0}", e.Message.Message), e.Message.Sender, JmShared.MessageType.Error);
        }

        /// <summary>
        /// Updates the UI on connect and disconnect
        /// </summary>
        /// <param name="isDisconnect"></param>
        private void DoWhenConnect(bool isDisconnect)
        {
            //Gtk.Application.Invoke (delegate {
            if (isDisconnect)
            {
                bool removeChatControl = false;
                foreach (var v in this.ChatBox.Children)
                {
                    if (v.GetType() == typeof (ChatControl))
                    {
                        removeChatControl = true;
                        break;
                    }
                }
                this.bnConfig.Sensitive = false;
                if (removeChatControl && ChatControl != null)
                {
                    this.ChatBox.Remove(ChatControl);
                    this.ChatBox.PackStart(LoginWidget, true, false, 0);
                }
                this.avatarBox.Visible = false;
                this.LoginWidget.DisableLoginButton();
                if (Client.CurrentUserInfo != null && ChatControl != null)
                    this.ChatControl.WriteLog("Main chat");

                if (Login.IsUserConnecting && !Login.IsRetrying && ChatControl != null)
                    this.ChatControl.Reset();
                LoginWidget.EnableLoginButton();
            }
            else
            {
                Login l = null;
                foreach (var v in ChatBox.Children)
                {
                    if (v.GetType() == typeof (Login))
                        l = (Login) v;
                    this.ChatBox.Remove(l);
                }

                this.avatarBox.Visible = true;

                ChatBox.PackStart(ChatControl, true, true, 0);
                //			foreach(var v in ChatBox.Children)
                //			{
                //				if(v.GetType()== typeof(Login))
                //					v.Visible=false;
                //				else if(v.GetType()==typeof(ChatControl))
                //				{
                //					v.Visible=true;
                //
                //				}
                //			}



                if (Client.CurrentUserInfo.UserName != Client.LastConnectedUserName)
                    this.ChatControl.Reset();
                Client.LastConnectedUserName = Client.CurrentUserInfo.UserName;
                Client.Reset();

                //				if (this.ConversationGrid.Children.Contains(loginWindow))
                //				{
                //					this.ChatControl.Visibility = Visibility.Visible;
                //					this.loginWindow.Visibility = Visibility.Hidden;
                //				}
                //				this.ChatControl.AvalonMessage.Focus();
                //				this.ChatControl.AvalonMessage.IsEnabled = true;
                this.Title = "Jive Messenger" + " - " + (String.IsNullOrEmpty(Client.CurrentUserInfo.NickName) ?
                                                             Client.CurrentUserInfo.UserName : Client.CurrentUserInfo.NickName + " <" + Client.CurrentUserInfo.UserName + ">");


                if (!this._firstConnect)
                    this.ChatControl.SessionID++;

                Login.StopRetrying = false;
                this._firstConnect = false;
                StaticSettings.Serialize();
                bnConfig.Sensitive = true;
                this.ChatControl.FocusMessageBox();
            }
            //});	

        }



        /// <summary>
        /// Displays the message to the group chat control.
        /// </summary>
        /// <param name="msg">Message.</param>
        /// <param name="sender">Sender.</param>
        /// <param name="mt">Mt.</param>
        private void DisplayMessage(object msg, UserInfo sender, JmShared.MessageType mt)
        {
            Gtk.Application.Invoke(delegate
                {
                    this.ChatControl.DisplayMessage(msg.ToString(), sender, mt);
                });
        }

        /// <summary>
        /// Starts the connect thread.
        /// </summary>
        private void StartConnectThread()
        {
            Thread t = new Thread(() =>
                {
                    if (!Client.Connect(false))
                    {
                        Gtk.Application.Invoke(delegate
                            {
                                this.DoWhenConnect(true);
                            });
                    }
                })
                {
                    Name = "ConnectThread"
                };
            t.Start();
        }

        /// <summary>
        /// Starts the reconnect thread.
        /// </summary>
        private void StartReconnectThread()
        {
            Thread t = new Thread(() =>
                {
                    Thread.Sleep(500);
                    for (int count = 0; count < 25; count++)
                    {
                        if (Login.StopRetrying)
                        {
                            Login.StopRetrying = false;
                            return;
                        }
                        int sleepTime = 5 + (5*count);
                        //var start = DateTime.UtcNow;
                        //var endTime = start.AddSeconds(sleepTime);
                        while (true)
                        {
                            if (Login.IsUserConnecting)
                                return;
                            this.LoginWidget.SetStatusText(String.Format("Retrying in {0} seconds.", sleepTime.ToString()));
                            if (sleepTime <= 0)
                            {
                                break;
                            }
                            sleepTime--;
                            Thread.Sleep(1000);
                        }
                        this.LoginWidget.SetStatusText("Connecting");
                        Login.IsRetrying = true;

                        if (Client.Connect(true))
                        {
                            Login.IsRetrying = false;

                            return;
                        }
                    }
                    ChatControl.Reset();
                })
                {
                    Name = "ReconnectThread"
                };
            t.Start();
        }

        /// <summary>
        /// Handles the process message event.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">E.</param>
        private void HandleProcessMessageEvent(object sender, WriteMessageEventArgs e)
        {
            ChatMessage msg = e.Message;
            switch (msg.MessageType)
            {
                case JmShared.MessageType.LoginFailed:
                    {

                        Login.StopRetrying = true;
                        Client.CurrentUserInfo = null;
                        break;
                    }
                case JmShared.MessageType.LoginSuccessful:
                    {
                        Login.IsUserConnecting = false;

                        Client.CurrentUserInfo = msg.Sender;
                        Gtk.Application.Invoke(delegate
                            {
                                this.LoginWidget.SetStatusText("");
                                this.DoWhenConnect(false);
                                Thread.Sleep(50);
                                AddUsers(msg);

                                this.DisplayMessage(Client.CurrentUserInfo.PrettyName + " has been added to the conversation.", Client.CurrentUserInfo, msg.MessageType);
                            });
                        Client.IsLoggedIn = true;

                        SetPmWindowStatus(msg.UserInfo, false);
                        //  Thread.Sleep(StaticMethods.RandomNumber(100, 500));
                        Client.BroadcastEmoticons();
                        Client.SendStatusMessage(MessageType.UpdateDeviceStatus, StaticMethods.ObjectToString<DeviceInfo>(new DeviceInfo
                            {
                                OfflineMessagesAgeLimit = StaticSettings.SettingOfflineMessagesAgeLimit,
                                OfflineMessagesCountLimit = StaticSettings.SettingOfflineMessagesCountLimit,
                                StoreMessagesWhileOffline = StaticSettings.SettingStoreMessagesWhileOffline,

                            }));
                        break;
                    }
                case JmShared.MessageType.Authenticated:
                    {
                        Gtk.Application.Invoke(delegate
                            {
                                AddUsers(msg);
                                if (Client.ConnectedClients.Values.ToList().FindAll(x => x.UserName == msg.Sender.UserName).Count == 1)
                                    this.DisplayMessage(msg.Sender.PrettyName + " has been added to the conversation.", msg.Sender, msg.MessageType);
                                if (!msg.Sender.Equals(Client.CurrentUserInfo)) //msg.Sender.UserName != Client.CurrentUserInfo.UserName)
                                {
                                    //                            NotifyMessage nMsg = new NotifyMessage("Images/GreenSkin.png", "Jive Messenger", String.Format("{0} has just logged on", msg.Sender.PrettyName),
                                    //                                                                           () =>
                                    //                                                                           OpenPM(msg.Sender));
                                    //                            this.NotifyMessageMgr.EnqueueMessage(nMsg);
                                    //  this.ChatControl.FocusTextBox();
                                }
                            });
                        SetPmWindowStatus(msg.UserInfo, false);
                        Thread.Sleep(StaticMethods.RandomNumber(100, 500));
                        Client.BroadcastEmoticons();
                        break;
                    }
                case JmShared.MessageType.Message:
                    {
                        Gtk.Application.Invoke(delegate
                            {
                                this.DisplayMessage(msg.Message, msg.Sender, msg.MessageType);
                                //                                if (!StaticMethods.ApplicationIsActivated() && Properties.Settings.Default.SettingFlashWindowOnNewMessage)
                                //                                {
                                //                                    this.FlashWindow();
                                //                                    IsFlashing = true;
                                //                                    
                                //                                }
                            });
                        break;
                    }
                case JmShared.MessageType.PM:
                    {
                        Gtk.Application.Invoke(delegate
                            {
                                OpenPM(msg);
                            });
                        break;
                    }
                case JmShared.MessageType.Hello:
                    {
                        Gtk.Application.Invoke(delegate
                            {
                                AddUsers(msg);
                                foreach (UserInfo u in msg.UserInfo)
                                {
                                    Button btn = this.GetAvatarButtonForUser(u);
                                    if (btn == null)
                                        return;
                                    AvatarWidget aw = btn.Child as AvatarWidget;
                                    if (aw != null)
                                        aw.UpdateStatus(u);
                                }
                            });
                        break;
                    }
                case JmShared.MessageType.UpdateUserInfo:
                    {
                        Gtk.Application.Invoke(delegate
                            {
                                AddUsers(msg);
                            });
                        break;
                    }
                case JmShared.MessageType.Logout:
                    {
                        Gtk.Application.Invoke(delegate
                            {
                                RemoveUser(msg.UserInfo);
                            });
                        SetPmWindowStatus(msg.UserInfo, true);
                        break;
                    }
                case JmShared.MessageType.IsWriting:
                    {
                        //this.Dispatcher.Invoke((Action)(() =>
                        //{
                        UpdateNowWriting(msg);
                        //}));
                        break;
                    }
                case JmShared.MessageType.Busy:
                case JmShared.MessageType.AFK:
                    {
                        Gtk.Application.Invoke(delegate
                            {
                                Button btn = this.GetAvatarButtonForUser(msg.Sender);
                                if (btn == null)
                                    return;
                                AvatarWidget aw = btn.Child as AvatarWidget;
                                if (aw != null)
                                    aw.UpdateStatus(msg.Sender);
                            });
                        break;
                    }
                case JmShared.MessageType.Nudge:
                    {
                        if (!StaticSettings.SettingEnableNudge)
                            break;

                        Gtk.Application.Invoke(delegate
                            {
                                //                                if (!StaticMethods.ApplicationIsActivated() && Properties.Settings.Default.SettingFlashWindowOnNewMessage)
                                //                                {
                                //                                    this.FlashWindow();
                                //                                    IsFlashing = true;
                                //                                }
                                if (msg.Message == "PM")
                                {
                                    msg.Message = "";
                                    if (!this.OpenPmWindows.ContainsKey(msg.Sender.UserName))
                                        OpenPM(msg);


                                    if (this.OpenPmWindows.ContainsKey(msg.Sender.UserName))
                                    {
                                        PrivateMessage pm = this.OpenPmWindows[msg.Sender.UserName];
                                        pm.Nudge();

                                        if (!msg.Sender.Equals(Client.CurrentUserInfo)) //user.UserName != Client.CurrentUserInfo.UserName)
                                            pm.DisplayMessage(string.Format("{0} just sent you a nudge.", msg.Sender.PrettyName), msg.Sender, JmShared.MessageType.Nudge);
                                    }
                                }
                                else
                                {
                                    if (msg.Sender.UserName != Client.CurrentUserInfo.UserName)
                                    {
                                        this.DisplayMessage(string.Format("{0} just sent you a nudge.", msg.Sender.PrettyName), msg.Sender, JmShared.MessageType.Nudge);
                                    }
                                    this.Nudge();
                                }
                            });
                        break;
                    }
                case JmShared.MessageType.BroadcastEmoticons:
                    {
                        ProcessCustomContactEmoticons(msg);
                        break;
                    }
                case JmShared.MessageType.RequestingEmoticon:
                    {
                        if (!StaticSettings.CustomEmoticons.ContainsKey(msg.Message))
                            break;
                        if (String.IsNullOrEmpty(StaticSettings.CustomEmoticons[msg.Message]))
                            break;
                        ContactEmoticon ce = new ContactEmoticon
                            {
                                KeyWord = msg.Message,
                                Emoticon = Convert.ToBase64String(ImageManipulation.LoadImageData(StaticSettings.CustomEmoticons[msg.Message])),
                                Extension = System.IO.Path.GetExtension(StaticSettings.CustomEmoticons[msg.Message])
                            };
                        msg.Message = StaticMethods.ObjectToString<ContactEmoticon>(ce);
                        msg.MessageType = JmShared.MessageType.SendingEmoticon;
                        UserInfo receiver = msg.Sender;
                        msg.UserInfo = new UserInfo[1];
                        msg.UserInfo[0] = receiver;
                        msg.Sender = Client.CurrentUserInfo;
                        Client.SendMessage(msg);
                        break;
                    }
                case JmShared.MessageType.SendingEmoticon:
                    {
                        //this.Dispatcher.Invoke((Action)(() =>
                        //{
                        ReceiveEmoticon(msg);
                        //}));
                        break;
                    }
            }
        }

        /// <summary>
        /// Receives a custom contact emoticon as a chatmessage, saves it to disk and stores the reference.
        /// </summary>
        /// <param name="msg">Message.</param>
        private void ReceiveEmoticon(ChatMessage msg)
        {
            ContactEmoticon ce = StaticMethods.StringToContactEmoticon(msg.Message);
            if (String.IsNullOrEmpty(ce.KeyWord))
                return;
            string savePath = StaticSettings.CustomContactEmoticonFolder;
            if (!Directory.Exists(savePath))
                Directory.CreateDirectory(savePath);
            //   savePath += "Icons_" + msg.Sender + Path.DirectorySeparatorChar;
            savePath = StaticMethods.CombinePath(savePath, "Icons_" + msg.Sender);
            if (!Directory.Exists(savePath))
                Directory.CreateDirectory(savePath);
            //savePath += StaticMethods.FileNameFriendly(ce.KeyWord) + ".gif";
            savePath = StaticMethods.CombinePath(savePath, StaticMethods.FileNameFriendly(ce.KeyWord) + ce.Extension);
            try
            {
                ImageManipulation.SaveImageData(Convert.FromBase64String(ce.Emoticon), savePath);
            }
            catch (IOException err)
            {
                this.DisplayMessage(String.Format("Error saving custom emoticon from {0} : ", msg.Sender) + err.Message, Client.CurrentUserInfo, JmShared.MessageType.Error);
                NetworkComms.Logger.ErrorException(String.Format("Error saving custom emoticon from {0} : ", msg.Sender), err);
                return;
            }
            if (StaticSettings.CustomContactEmoticons.ContainsKey(msg.Sender.UserID))
            {
                Dictionary<string, string> cce = StaticSettings.CustomContactEmoticons[msg.Sender.UserID];

                if (cce.ContainsKey(ce.KeyWord))
                    cce[ce.KeyWord] = savePath;
            }
        }

        /// <summary>
        /// Process other users custom emoticons
        /// </summary>
        /// <param name="message"></param>
        private void ProcessCustomContactEmoticons(ChatMessage message)
        {
            //if (!UserInfo.IsValidMacID(message.Sender.UserID))
            //    return;
            List<string> customEmoticons = StaticMethods.StringToList<string>(message.Message);
            foreach (string str in customEmoticons)
            {
                if (!StaticSettings.CustomContactEmoticons.ContainsKey(message.Sender.UserID))
                {
                    StaticSettings.CustomContactEmoticons[message.Sender.UserID] = new SerializableDictionary<string, string>(); // List<Emoticons.ContactEmoticon>();
                }
                bool requestEmoticon = false;
                if (!StaticSettings.CustomContactEmoticons[message.Sender.UserID].ContainsKey(str))
                {
                    requestEmoticon = true;
                }
                else if (String.IsNullOrEmpty(StaticSettings.CustomContactEmoticons[message.Sender.UserID][str]))
                    requestEmoticon = true;
                if (requestEmoticon)
                {
                    //save a reference to the emoticon in the library
                    if (!StaticSettings.CustomContactEmoticons[message.Sender.UserID].ContainsKey(str))
                        StaticSettings.CustomContactEmoticons[message.Sender.UserID].Add(str, ""); //new Emoticons.ContactEmoticon { KeyWord = str });
                    //Send a message to the user requesting the user send the emoticon so we can save it to disk
                    ChatMessage msg = new ChatMessage();
                    msg.MessageType = JmShared.MessageType.RequestingEmoticon;
                    msg.Message = str;
                    UserInfo receiver = message.Sender;
                    msg.Sender = Client.CurrentUserInfo;
                    msg.UserInfo = new UserInfo[1];
                    msg.UserInfo[0] = receiver;
                    if (!Client.SendMessage(msg))
                    {
                        Gtk.Application.Invoke(delegate
                            {
                                MessageBox.Show("Error sending emoticon request");
                            });
                    }
                    Thread.Sleep(50);
                }
            }
            List<string> toRemove = new List<string>();
            if (StaticSettings.CustomContactEmoticons.ContainsKey(message.Sender.UserID))
            {
                foreach (KeyValuePair<string, string> kvp in StaticSettings.CustomContactEmoticons[message.Sender.UserID])
                {
                    if (!customEmoticons.Contains(kvp.Key))
                        toRemove.Add(kvp.Key);
                }
            }
            foreach (string str in toRemove)
                StaticSettings.CustomContactEmoticons[message.Sender.UserID].Remove(str);
        }

        /// <summary>
        /// Saves info on clients who are currently typing
        /// </summary>
        private void UpdateNowWriting(ChatMessage msg)
        {
            if (Client.CurrentUserInfo == null)
                return;
            if (msg.Sender.UserName == Client.CurrentUserInfo.UserName)
                return;
            if (msg.Message == "PM")
            {
                PrivateMessage pm;
                if (!this.OpenPmWindows.TryGetValue(msg.Sender.UserName, out pm))
                    return;

                pm.LastReceivedMessageTyping[msg.Sender.UserID] = DateTime.Now;
            }
            else
            {
                ChatControl.LastReceivedMessageTyping[msg.Sender.UserID] = DateTime.Now;
            }
        }

        /// <summary>
        /// Removes a user when they log off
        /// </summary>
        /// <param name="users"></param>
        private void RemoveUser(IEnumerable<UserInfo> users)
        {
            if (users == null)
                return;

            foreach (UserInfo user in users.Where(user => user != null))
            {
                if (user.Equals(Client.CurrentUserInfo)) // don't remove our own connected user.
                    continue;
                for (int c = 0; c < this.avatarBox.Children.Length; c++)
                {
                    var child = this.avatarBox.Children[c];
                    if (child == null || child.GetType() != typeof (Button))
                        continue;

                    Button b = (Button) child;
                    UserInfo ui = (UserInfo) b.Data.Values.Cast<UserInfo>().ToList().First();
                    if (ui == null || !ui.Equals(user))
                        continue;
                    UserInfo user1 = user;
                    if (Client.ConnectedClients.Values.ToList().FindAll(x => x.UserName == user1.UserName).Count > 1)
                    {
                        UserInfo nextUserInfo = Client.ConnectedClients.Values.ToList().Find(x => x.UserName == user.UserName && x.UserID != user.UserID);
                        if (nextUserInfo != null)
                        {
                            b.Data.Clear();
                            b.Data.Add(nextUserInfo.UserID, nextUserInfo);
                        }
                    }
                    else
                    {
                        this.avatarBox.Remove(child);
                        this.DisplayMessage(user.PrettyName + " has left the conversation!", user, JmShared.MessageType.Logout);
                    }
                }
                if (Client.ConnectedClients.ContainsKey(user.UserID))
                    Client.ConnectedClients.Remove(user.UserID);

                if (this.ChatControl.LastReceivedMessageTyping.ContainsKey(user.UserID))
                    this.ChatControl.LastReceivedMessageTyping.Remove(user.UserID);
                foreach (PrivateMessage pm in this.OpenPmWindows.Values)
                {
                    if (pm.LastReceivedMessageTyping.ContainsKey(user.UserID))
                        pm.LastReceivedMessageTyping.Remove(user.UserID);
                }
            }
        }

        /// Opens a private message window to the specified target user
        /// </summary>
        /// <param name="target"></param>
        private void OpenPM(UserInfo target)
        {
            PrivateMessage pm;
            if (this.OpenPmWindows.ContainsKey(target.UserName))
            {
                pm = this.OpenPmWindows[target.UserName];
                //  if (pm.GdkWindow.State == WindowState.Iconified)
                //  {
                pm.ShowNow(); // = StateType.Normal;
                // }
                //                pm.SetActive();
            }
            else
            {
                pm = new PrivateMessage(target);
                AddPMEventHandlers(pm);
                this.OpenPmWindows.Add(target.UserName, pm);
                //                pm.Owner = this;
                //                pm.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                pm.ShowNow();
                //                pm.SetActive();
            }
        }

        /// <summary>
        /// Receives and processes a private message.
        /// </summary>
        /// <param name="msg"></param>
        private void OpenPM(ChatMessage msg)
        {
            PrivateMessage pm;
            if (this.OpenPmWindows.ContainsKey(msg.Sender.UserName))
            {
                pm = this.OpenPmWindows[msg.Sender.UserName];
            }
            else
            {
                pm = new PrivateMessage(msg.Sender);
                string message = String.Format("{0} says:\n {1}", msg.Sender.PrettyName, msg.Message);
                if (msg.MessageType == JmShared.MessageType.Nudge)
                    message = String.Format("{0} just sent you a nudge.", msg.Sender.PrettyName);
                //            NotifyMessage nMsg = new NotifyMessage("Images/GreenSkin.png", "Jive Messenger", message, () => OpenPM(msg.Sender));
                //            this.NotifyMessageMgr.EnqueueMessage(nMsg);
                //            this.ChatControl.AvalonMessage.Focus();
                AddPMEventHandlers(pm);
                //            pm.WindowStartupLocation = WindowStartupLocation.CenterOwner;
                pm.Show();
                //            pm.FlashWindow();
                //            pm.IsFlashing = true;
                this.OpenPmWindows.Add(msg.Sender.UserName, pm);
            }
            if (msg.MessageType == JmShared.MessageType.PM)
                pm.DisplayMessage(msg);
        }

        /// <summary>
        /// Sets the pm window status.
        /// </summary>
        /// <param name="users">Users.</param>
        /// <param name="isOffline">If set to <c>true</c> is offline.</param>
        private void SetPmWindowStatus(IEnumerable<UserInfo> users, bool isOffline)
        {
            foreach (UserInfo user in users)
            {
                if (this.OpenPmWindows.ContainsKey(user.UserName))
                {
                    PrivateMessage pm = this.OpenPmWindows[user.UserName];
                    Gtk.Application.Invoke(delegate
                        {
                            pm.SetStatus(isOffline);
                        });
                }
            }
        }

        /// <summary>
        /// Adds users to the list and UI
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        private void AddUsers(ChatMessage message)
        {
            UserInfo[] users = message.UserInfo;


            foreach (UserInfo u in users)
            {
                if (u == null)
                    continue;
                Client.ConnectedClients[u.UserID] = u;
                // if (Client.TextTags.ContainsKey(u.UserID))
                //    this.ChatControl.RemoveTextTag(Client.TextTags[u.UserID]);
                //            if (!Client.TextTags.ContainsKey(u.UserID))
                //            {
                if (!Client.TextTagCount.ContainsKey(u.UserID))
                    Client.TextTagCount.Add(u.UserID, 0);

                Client.TextTagCount[u.UserID] = Client.TextTagCount[u.UserID] + 1;
                int num = Client.TextTagCount[u.UserID];
                Client.TextTags[u.UserID + "_" + num] = Client.GetTextTagForUser(u, num);
                this.ChatControl.AddTextTag(Client.TextTags[u.UserID + "_" + num]);

                //            }
                //            else
                //            {
                //                Client.TextTags[u.UserID] = Client.GetTextTagForUser(u);
                //                this.ChatControl.ModifyTextTag(u.UserID,Client.TextTags[u.UserID]);
                //            }
                if (GetAvatarButtonForUser(u) == null)
                {
                    Button btn = new Button(); // Button.NewWithMnemonic(u.PrettyName);

                    AvatarWidget aw = new AvatarWidget(u);
                    aw.Visible = true;
                    btn.Add(aw);


                    btn.HeightRequest = 60;
                    btn.WidthRequest = 150;

                    btn.Visible = true;
                    btn.Data.Add(u.UserID, u);
                    btn.QueryTooltip += (s, ev) => Gtk.Application.Invoke(delegate
                        {
                            ev.Tooltip.Text = u.UserName;
                        });
                    //                btn.ButtonPressEvent += HandleButtonPressEvent;
                    btn.WidgetEvent += HandleWidgetEvent;
                    //                btn.Clicked += (s, ev) => 
                    //                {
                    //                    UserInfo gfg = null;                         
                    //                    OpenPM(gfg);
                    //                };
                    // avatarButtonBox.PackStart(btn, false, false, 0);
                    avatarBox.PackStart(btn, false, false, 0);
                }
                else //If user is already online, just update info
                {
                    Button btn = GetAvatarButtonForUser(u);
                    if (btn != null)
                    {
                        // Label l = btn.Child as Label;
                        //  l.Text = u.PrettyName;
                        AvatarWidget aw = btn.Child as AvatarWidget;
                        if (aw != null)
                        {
                            aw.UpdateStatus(u);
                        }

                        //                    TextBlock tb = btn.FindName("UserName") as TextBlock;
                        //                    if (tb != null)
                        //                        tb.Text = u.PrettyName;

                    }
                }
            }
        }

        private void HandleWidgetEvent(object o, WidgetEventArgs args)
        {
            Gdk.Event gdkEvent;
            Button btn = o as Button;
            Gtk.Widget wdgt = (Gtk.Widget) o;
            foreach (object argObj in args.Args)
            {
                // For now, we'll filter ALL Widget events except the ones that we (plan/know_how) to handle
                System.Type sysType = argObj.GetType();
                if ((sysType == typeof (Gdk.EventButton))
                    || (sysType == typeof (Gdk.EventKey)))
                {
                    gdkEvent = (Gdk.Event) argObj;
                    switch (gdkEvent.Type)
                    {
                        case Gdk.EventType.ButtonPress:
                            // handle the event as desired here...
                            break;

                        case Gdk.EventType.TwoButtonPress:
                            UserInfo info = (UserInfo) btn.Data.Values.Cast<UserInfo>().ToList().First();
                            
                            //if (info.Equals(Client.CurrentUserInfo))
                            //    return;
                            OpenPM(info);
                            break;
                    }
                }
            }
        }

        //    void HandleButtonPressEvent (object o, ButtonPressEventArgs args)
        //    {
        //            EventButton b = args.Event;
        //        Button btn = o as Button;
        //            if (b.Type == EventType.TwoButtonPress)
        //            {
        //            UserInfo info = (UserInfo)btn.Data.Values.Cast<UserInfo>().ToList().First();;
        //                if (info.Equals(Client.CurrentUserInfo))
        //                    return;
        //                OpenPM(info);
        //            }
        //    }
        private Button GetAvatarButtonForUser(UserInfo info)
        {
            foreach (var v in avatarBox.Children)
            {
                if (v.GetType() != typeof (Button))
                    continue;
                Button b = (Button) v;
                if (b.Data == null)
                    continue;

                UserInfo usr = (UserInfo) b.Data.Values.Cast<UserInfo>().ToList().First();
                if (usr != null)
                {
                    if (usr.UserName == info.UserName)
                        return b;
                }
            }
            return null;
        }

        /// <summary>
        /// Triggers on disconnect from the server.
        /// </summary>y>
        private void HandleClientDisconnectedEvent(object sender, EventArgs e)
        {
            Client.IsLoggedIn = false;
            this.DoWhenConnect(true);

            if (!Login.IsUserConnecting && !Login.IsRetrying)
            {
                this.StartReconnectThread();
            }
            Login.IsUserConnecting = false;
        }

        private void OnDeleteEvent(object sender, DeleteEventArgs e)
        {
            int x, y;
            int posX, posY;
            this.GetSize(out x, out y);
            this.GetPosition(out posX, out posY);
            StaticSettings.WindowX = posX;
            StaticSettings.WindowY = posY;

            StaticSettings.WindowSizeX = x;
            StaticSettings.WindowSizeY = y;

            if (!StaticSettings.RememberMe)
                StaticSettings.Username = "";
            if (!StaticSettings.RememberPassword)
                StaticSettings.Password = "";
            SerializableDictionary<string, SerializableDictionary<string, string>> dicToSave = new SerializableDictionary<string, SerializableDictionary<string, string>>();
            foreach (KeyValuePair<string, SerializableDictionary<string, string>> kvp in StaticSettings.CustomContactEmoticons)
            {
                SerializableDictionary<string, string> validEmoticons = new SerializableDictionary<string, string>();
                foreach (KeyValuePair<string, string> kv in kvp.Value)
                {
                    if (!String.IsNullOrEmpty(kv.Value))
                        validEmoticons.Add(kv.Key, kv.Value);
                }
                dicToSave.Add(kvp.Key, validEmoticons);
            }
            StaticSettings.CustomContactEmoticons = dicToSave;
            StaticSettings.Serialize();
            Login.IsUserConnecting = true;
            NetworkComms.Shutdown();
            Application.Quit();
            e.RetVal = true;
        }

        /// <summary>
        /// Initialize logging
        /// </summary>
        private static void SetLogging()
        {

            string dir = StaticMethods.CombinePath(System.AppDomain.CurrentDomain.BaseDirectory, Environment.UserName.Replace(" ", ""));
            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);

            LoggingConfiguration logConfig = new LoggingConfiguration();

            FileTarget fileTarget = new FileTarget
                {
                    FileName = "${basedir}/" + Environment.UserName.Replace(" ", "") + "/log.txt",
                    Layout = "${date:format=dd-MMM-yyyy HH\\:mm\\:ss} [${level}] - ${message} - ${exception}"
                };
            logConfig.AddTarget("file", fileTarget);
            NLog.LogLevel l = null;
            switch (StaticSettings.SettingDebugLogLevel)
            {
                case "Fatal":
                    l = NLog.LogLevel.Fatal;
                    break;
                case "Error":
                    l = NLog.LogLevel.Error;
                    break;
                case "Warn":
                    l = NLog.LogLevel.Warn;
                    break;
                case "Info":
                    l = NLog.LogLevel.Info;
                    break;
                case "Debug":
                    l = NLog.LogLevel.Debug;
                    break;
                case "Trace":
                    l = NLog.LogLevel.Trace;
                    break;
            }
            if (String.IsNullOrEmpty(StaticSettings.SettingDebugLogLevel))
                l = NLog.LogLevel.Error;
            if (!StaticSettings.SettingEnableDebugLogging)
                l = NLog.LogLevel.Off;
            if (l == null)
                return;
            logConfig.LoggingRules.Add(new LoggingRule("*", l, fileTarget));

            NetworkComms.EnableLogging(logConfig);
        }
    }

[Serializable]
[ProtoContract]
public struct ContactEmoticon
{
    [ProtoMember(1)]
    public string KeyWord { get; set; }

    [ProtoMember(2)]
    public string Emoticon { get; set; }

    [ProtoMember(3)]
    public string Extension { get; set; }
}
}
